package cn.graydove.bot.service.impl;

import cn.graydove.bot.service.SmsService;
import cn.graydove.bot.support.ApiProperties;
import cn.graydove.bot.utils.CommonUtils;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
public class SmsServiceImpl implements SmsService {
    private ConcurrentHashMap<Long, Valid> validCodes = new ConcurrentHashMap<>();

    private ApiProperties apiProperties;

    public SmsServiceImpl(ApiProperties apiProperties) {
        this.apiProperties = apiProperties;
    }

    @Override
    public boolean sendVailCode(Long phoneNumber) {
        Valid v = validCodes.get(phoneNumber);
        if (v != null && !v.isOverdue(60 * 1000L)) {
            return false;
        }

        int validNumber = Math.abs(new Random().nextInt()) % 900000;
        String validCode = String.valueOf(validNumber + 100000);

        Map<String, String> valid = new HashMap<>();
        valid.put("code", validCode);
        Map s = send(valid, phoneNumber);
        if (s != null) {
            if ("OK".equals(s.get("Code"))) {
                validCodes.put(phoneNumber, new Valid(validCode));
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean verify(String validCode, Long phoneNumber) {
        if (validCode == null) {
            return false;
        }
        Valid valid = validCodes.get(phoneNumber);
        if (valid == null) {
            return false;
        }
        if (valid.isOverdue(apiProperties.getSms().getTtl())) {
            validCodes.remove(phoneNumber);
            return false;
        }
        boolean verify = valid.verify(validCode);
        if (verify) {
            validCodes.remove(phoneNumber);
        }
        return verify;
    }

    private Map send(Object msg, Long phoneNumber){
        DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou",
                apiProperties.getAccessKeyId(),
                apiProperties.getAccessSecret());
        IAcsClient client = new DefaultAcsClient(profile);

        CommonRequest request = new CommonRequest();
        request.setMethod(MethodType.POST);
        request.setDomain("dysmsapi.aliyuncs.com");
        request.setVersion("2017-05-25");
        request.setAction("SendSms");
        request.putQueryParameter("RegionId", "cn-hangzhou");
        request.putQueryParameter("PhoneNumbers", phoneNumber.toString());
        request.putQueryParameter("SignName", apiProperties.getSms().getSignName());
        request.putQueryParameter("TemplateCode", apiProperties.getSms().getTemplateCode());
        try {
            request.putQueryParameter("TemplateParam", CommonUtils.objectMapper.writeValueAsString(msg));
        } catch (JsonProcessingException e) {
            log.error(e.getMessage());
        }
        try {
            CommonResponse response = client.getCommonResponse(request);
            String msgStr = response.getData();
            log.info(msgStr);
            return CommonUtils.objectMapper.readValue(msgStr, HashMap.class);

        } catch (ClientException e) {
            log.error(e.getErrMsg());
        } catch (IOException e) {
            log.error(e.getMessage());
        }
        return null;
    }

    private static class Valid {
        private String code;
        private Date date;

        Valid(String code) {
            this.code = code;
            this.date = new Date();
        }

        boolean isOverdue(Long ttl) {
            Date now = new Date();
            long time = now.getTime() - date.getTime();
            return time > ttl;
        }

        boolean verify(String code) {
            return StringUtils.equals(code, this.code);
        }
    }
}
